package com.googlecode.tda367.denty.core.dynamicbody;

import static com.googlecode.tda367.denty.constants.DynamicConstant.JUMP_FORCE;
import static com.googlecode.tda367.denty.constants.DynamicConstant.VELOCITY_H_INC;
import static com.googlecode.tda367.denty.constants.DynamicConstant.VELOCITY_H_MAX;
import static com.googlecode.tda367.denty.constants.Hit.GOAL;
import static com.googlecode.tda367.denty.constants.Hit.NORMAL_ENEMY;
import static com.googlecode.tda367.denty.constants.Hit.THROWABLE_BLOCK_HIGH_SPEED;
import static com.googlecode.tda367.denty.constants.StateConstant.MOVING_LEFT;
import static com.googlecode.tda367.denty.constants.StateConstant.MOVING_RIGHT;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

import com.googlecode.tda367.denty.constants.Constants;
import com.googlecode.tda367.denty.constants.DynamicConstant;
import com.googlecode.tda367.denty.constants.Hit;
import com.googlecode.tda367.denty.constants.StateConstant;
import com.googlecode.tda367.denty.util.PhysicalBodiesUtil;

public class Denty implements MoveableBody{
	private InnerBody body;
	private final PropertyChangeSupport pcs;
	private Fixture dentyTopFixture;
	private Fixture dentyBottomFixture;
	
	public Denty(float x, float y, World physicsWorld) {
		Body physicalBody = this.definePhysicalRepresentation(physicsWorld, new Vec2(x,y));
		this.body = new InnerBody(physicalBody, Constants.DENTY_MAX_HP);
        this.defineConstants();
        this.pcs = new PropertyChangeSupport(this);
        this.pcs.firePropertyChange("hp", 0, body.getHP());
	}
	
	private Body definePhysicalRepresentation(World world, Vec2 position){
		BodyDef bodyDef = PhysicalBodiesUtil.createBodyDef(position, BodyType.DYNAMIC, this, true, 2);
		Body body = world.createBody(bodyDef);
		float density = 0.6f;
		body.createFixture(this.createMiddleShape(), density);
		this.dentyTopFixture = body.createFixture(this.createTopShape(), density);
		this.dentyBottomFixture = body.createFixture(this.createBottomShape(density));
		return body;
	}
	
	private PolygonShape createMiddleShape(){
		return PhysicalBodiesUtil.createBoxShape(0.96f, 1.36f);
	}
	
	private PolygonShape createTopShape(){
		PolygonShape dentyTop = new PolygonShape();
		Vec2[] topVertices = {new Vec2(-0.3f, -0.98f), new Vec2(0.3f, -0.98f),
				new Vec2(0.48f, -0.7f), new Vec2(-0.48f, -0.7f)};		
		dentyTop.set(topVertices, topVertices.length);
		return dentyTop;
	}
	
	private FixtureDef createBottomShape(float density){
		PolygonShape dentyBottom = new PolygonShape();
		Vec2[] bottomVertices = {new Vec2(0.48f, 0.7f), new Vec2(0.3f, 0.98f),
				new Vec2(-0.3f, 0.98f), new Vec2(-0.48f, 0.7f)};		
		dentyBottom.set(bottomVertices, bottomVertices.length);
		FixtureDef bottomFixture = PhysicalBodiesUtil.createFixtureDef(dentyBottom, density, 0);
		bottomFixture.friction = 0.5f;
		return bottomFixture;
	}

	@Override
	public Vec2 getPosition(){
		return new Vec2(this.body.getPosition());
	}

	@Override
	public float getRotation() {
		return this.body.getRotation();
	}

	@Override
	public Vec2 getLinearVelocity() {
		return new Vec2(this.body.getLinearVelocity());
	}

	@Override
	public void applyForce(Vec2 force) {
		this.body.applyForce(force);
	}

	@Override
	public void applyVelocity(Vec2 velocity) {
		this.body.applyVelocity(velocity);
	}
		
	@Override
	public float getConstant(DynamicConstant dc) {
		return this.body.getConstant(dc);
	}
	
	public void addPropertyChangeListener(PropertyChangeListener listener){
		pcs.addPropertyChangeListener(listener);
	}
	
	public void removePropertyChangeListener(PropertyChangeListener listener){
		pcs.removePropertyChangeListener(listener);
	}
	
	private void defineConstants(){
		this.body.addDynamicConstant(VELOCITY_H_MAX, 10);
		this.body.addDynamicConstant(VELOCITY_H_INC, 1);
		this.body.addDynamicConstant(JUMP_FORCE, -1700);
		
		this.body.addAllowedState(MOVING_LEFT);
		this.body.addAllowedState(MOVING_RIGHT);
	}

	@Override
	public boolean getState(StateConstant sc) {
		return body.getState(sc);
	}

	@Override
	public void setState(StateConstant sc, boolean b) {
		this.body.setState(sc, b);
	}

	@Override
	public void beginContact(Fixture hitFixture, Collection<Hit> hitConstants) {
		int oldHP = this.body.getHP();
		if(hitConstants.contains(NORMAL_ENEMY)) {
			if(hitFixture == this.dentyBottomFixture) {
				this.applyForce(new Vec2(0, -2000));
			} else {
				this.body.changeHP(-25);
			}
		} else if(hitConstants.contains(GOAL)){
			this.body.resetHP();
		} else if(hitConstants.contains(THROWABLE_BLOCK_HIGH_SPEED)){
			this.body.changeHP(-25);
		}
		pcs.firePropertyChange("hp", oldHP, this.body.getHP());
	}

	@Override
	public Collection<Hit> getHitConstants(Fixture fixture) {
		List<Hit> hitConstants = new ArrayList<Hit>();
		hitConstants.add(Hit.DENTY);
		if(fixture == this.dentyTopFixture) {
			hitConstants.add(Hit.DENTY_TOP);
		} else if (fixture == this.dentyBottomFixture) {
			hitConstants.add(Hit.DENTY_BOTTOM);
		} else {
			hitConstants.add(Hit.DENTY_MIDDLE);
		}
		return hitConstants;
	}

	@Override
	public void destroy() {
		this.body.destroy();
		pcs.firePropertyChange("killed", 0, 100);
	}

	@Override
	public boolean isAlive() {
		return body.isAlive();
	}

	@Override
	public String getImagePath() {
		return "res/img/denty.png";
	}

	@Override
	public void endContact(Fixture hitFixture, Collection<Hit> hitConstants) {
		// TODO Auto-generated method stub
	}
	
	public void setAlive(boolean alive) {
		body.setAlive(alive);
	}
}